/*
 * @FileName: bsp_flash.c
 * @Author: GreyQiu qiushaogui@aikosolar.com
 * @Date: 2023-07-13 15:53:43
 * @LastEditors: GreyQiu qiushaogui@aikosolar.com
 * @LastEditTime: 2023-09-20 14:09:32
 * @Description: 
 * 本模块实现在芯片对FLASH操作，用户自行定义FLASH操作对象OBJ，自行制定OBJ的地址和大小
 * 注意！！ 本模块对上层应用屏蔽了EEPROM在FLASH中的具体位置
 * 用户仅认为eeprom基地址从0开始以及大小即可，无需考FLASH中的实际位置
 * 
 * Copyright (c) 2023, All Rights Reserved. 
 */

#include "bsp.h"
#include "log.h"

/**
 * @description: 初始化FLASH对象
 * @param obj 操作对象
 * @param base_addr 基地址 必须按页大小对齐
 * @param size 字节大小 必须按页大小的整数倍
 * @return 操作结果
 */
ErrorStatus bsp_flash_obj_init(flash_obj_t * obj, uint32_t base_addr, uint32_t size)
{
    // // 判断base_addr和size 是否合法
    // if (base_addr < FLASH_BASE_ADDR || (base_addr + size) > FLASH_PAGE_SIZE * FLASH_PAGE_NUM) {
    //     return ERROR; // 非法地址
    // }

    // if ((base_addr & (FLASH_PAGE_SIZE - 1)) != 0) { // 是否按页对齐
    //     return ERROR; // 非法地址
    // }

    // if ((size & (FLASH_PAGE_SIZE - 1)) != 0) { // 是否按页对齐
    //     return ERROR; // 非法地址
    // }

    obj->base_addr = base_addr;
    obj->size = size;

    return SUCCESS;
}

/**
 * @description: 擦除FLASH对象中的一页
 * @param obj 操作对象
 * @param page_addr 要擦除的页地址(相对) 必须按页对齐
 * @param page_num 要擦除的页数
 * @return 操作结果
 */
ErrorStatus bsp_flash_page_erase(flash_obj_t * obj, uint32_t page_addr, uint32_t page_num)
{
    ErrorStatus rtn = SUCCESS;
    // 判断obj是否已经初始化
    if (obj->base_addr < FLASH_BASE_ADDR) {
        return ERROR; // 非法地址
    }
    // 判断偏移地址是否合法
    if (page_addr >= obj->size) {
        return ERROR; // 非法地址
    }

    uint32_t addr = page_addr + obj->base_addr; // FLASH中真实的地址

    FLASH_Unlock();
    for (uint32_t i = 0; i < page_num; i++) {
        if (FLASH_EOP != FLASH_One_Page_Erase(addr + i * FLASH_PAGE_SIZE)) {
            rtn = ERROR;
        }
        bsp_wdg_feed();
    }
    FLASH_Lock();
    return rtn;
}

/**
 * @description: 对FLASH对象编程
 * @param obj 操作对象
 * @param address 相对于OBJ指定地址的偏移地址
 * @param pData 要编程的内容的地址
 * @param byteSize 要编程的字节数
 * @return 操作结果
 */
ErrorStatus bsp_flash_write(flash_obj_t * obj, uint32_t address, uint8_t * pData, uint32_t byteSize)
{
    ErrorStatus rtn = SUCCESS;

    // 判断obj是否已经初始化
    if (obj->base_addr < FLASH_BASE_ADDR) {
        log_debug("ERROR1...\n");
        return ERROR; // 非法地址
    }
    // 判断操作地址是否合法
    if (address >= obj->size || (address + byteSize) > obj->size) {
        log_debug("ERROR2...\n");
            return ERROR; // 非法地址
    }
    // // 判断是否跨页编程
    // if (address / FLASH_PAGE_SIZE != (address + byteSize - 1) / FLASH_PAGE_SIZE) {
    //     log_debug("ERROR3...\n");
    //     return ERROR; // 非法地址
    // }

    FLASH_Unlock();
    uint32_t addr = address + obj->base_addr; // FLASH中真实的地址
    // 按字编程
    for (uint32_t i = 0; i < byteSize; i += FLASH_WORD_SIZE) {
        if (FLASH_EOP != FLASH_Word_Program(addr, *((uint32_t *)pData)))
        {
            log_debug("ERROR4...\n");
            rtn = ERROR;
            break; // 编程错误
        }
        addr += FLASH_WORD_SIZE;
        pData += FLASH_WORD_SIZE;
        bsp_wdg_feed();
    }

    FLASH_Lock();

    return rtn;
}

/**
 * @description: 从FLASH对象读取
 * @param obj 操作对象
 * @param address 相对于OBJ指定地址的偏移地址
 * @param pData 要读取的内容的地址
 * @param byteSize 要读取的字节数
 * @return 操作结果
 */
ErrorStatus bsp_flash_read(flash_obj_t * obj, uint32_t address, uint8_t * pData, uint32_t byteSize)
{
    // 判断obj是否已经初始化
    if (obj->base_addr < FLASH_BASE_ADDR) {
        return ERROR; // 非法地址
    }
    // 判断操作地址是否合法
    if (address >= obj->size || (address + byteSize) > obj->size) {
        return ERROR; // 非法地址
    }

    const uint8_t * pDest = (const uint8_t *)(address + obj->base_addr);
    for (uint32_t i = 0; i < byteSize; i++) {
        *pData = *pDest;
        pData++;
        pDest++;
    }
    return SUCCESS;
}
